#ifndef __MCS__FUNCTION_FACTORY_COLLECTION__HPP
#define __MCS__FUNCTION_FACTORY_COLLECTION__HPP

#include "String.hpp"
#include "InvalidArgumentException.hpp"

static int operatorAdd(int a, int b) { return a + b; }
static int operatorSub(int a, int b) { return a - b; }
static int operatorMul(int a, int b) { return a * b; }
static int operatorDiv(int a, int b) { return a / b; }
static int operatorMod(int a, int b) { return a % b; }
static int operatorOr (int a, int b) { return a | b; }
static int operatorAnd(int a, int b) { return a & b; }
static int operatorXor(int a, int b) { return a ^ b; }
static int operatorShL(int a, int b) { return a << b; }
static int operatorShR(int a, int b) { return a >> b; }
static int operatorInv(int a) { return ~a; }
static int operatorNeg(int a) { return -a; }
static int operatorPos(int a) { return +a; }

static bool operatorBoolNot(bool a) { return !a; }
static bool operatorBoolAnd(bool a, bool b) { return a && b; }
static bool operatorBoolOr (bool a, bool b) { return a || b; }
static bool operatorBoolXor(bool a, bool b) { return (a && !b) || (b && !a); }

template<class T> bool operatorCmpLNGE(T a, T b) { return a < b; }
template<class T> bool operatorCmpGNLE(T a, T b) { return a > b; }
template<class T> bool operatorCmpENLG(T a, T b) { return a == b; }
template<class T> bool operatorCmpGENL(T a, T b) { return a >= b; }
template<class T> bool operatorCmpLENG(T a, T b) { return a <= b; }
template<class T> bool operatorCmpLGNE(T a, T b) { return a != b; }

template<class T> T operatorAssign(T x, T y) { (void)x; return y; }

static String operatorConcat(String const & s1, String const & s2)
{
	return s1 + s2;
}

template<class T> T functionIif(bool cond, T a, T b) { return cond ? a : b; }

template<class T> T functionAbs(T a) { return a < 0 ? -a : a; }
template<class T> T functionMin(T a, T b) { return a < b ? a : b; }
template<class T> T functionMax(T a, T b) { return a < b ? b : a; }

String functionLeft(String src, int n)
{
	if(n < 0)
		throw InvalidArgumentException("left(str,int)", 2, "count", new IntValue(n), "Cannot be negative");
	if(size_t(n) > src.size())
		throw InvalidArgumentException("left(str,int)", 2, "count", new IntValue(n), "Cannot be greater than string size");
	if(n == 0) return String();
	if(n == src.size()) return src;
	return src.substr(1, n);
}

String functionRight(String src, int n)
{
	if(n < 0)
		throw InvalidArgumentException("right(str,int)", 2, "count", new IntValue(n), "Cannot be negative");
	if(size_t(n) > src.size())
		throw InvalidArgumentException("right(str,int)", 2, "count", new IntValue(n), "Cannot be greater than string size");
	if(n == 0) return String();
	if(n == src.size()) return src;
	return src.substr(src.size() - n, n);	
}

String functionMid1(String src, int n)
{
	if(n < 0)
		throw InvalidArgumentException("mid(str,int)", 2, "pos", new IntValue(n), "Cannot be negative");
	if(size_t(n) > src.size())
		throw InvalidArgumentException("mid(str,int)", 2, "pos", new IntValue(n), "Cannot be greater than string size");
	if(n == 0) return src;
	if(n == src.size()) return String();
	return src.substr(n, src.size() - n);
}

String functionMid2(String src, int n, int k)
{
	if(n < 0)
		throw InvalidArgumentException("mid(str,int,int)", 2, "pos", new IntValue(n), "Cannot be negative");
	if(size_t(n) > src.size())
		throw InvalidArgumentException("mid(str,int)", 2, "pos", new IntValue(n), "Cannot be greater than string size");
	if(k < 0)
		throw InvalidArgumentException("mid(str,int,int)", 2, "count", new IntValue(k), "Cannot be negative");
	if(size_t(k) > src.size() - size_t(n))
		throw InvalidArgumentException("mid(str,int,int)", 2, "count", new IntValue(k), "Cannot be greater than length of the available string portion");
	return src.substr(n, k);
}

template<class T> String functionSubst(String const & templ, T val)
{
	std::stringstream str;
	bool hasPercent = false;
	static char const percentChar = '%';
	
	for(size_t j=0; j<templ.size(); ++j)
	{
		char c = templ.at(j);
		bool isPercent = (c == percentChar);

		if(hasPercent)
		{
			if(isPercent) str << percentChar; else str << val;
			hasPercent = false;
		}
		else
		{
			if(isPercent) hasPercent = true;
		}

		if(!isPercent) str << c;
	}
	if(hasPercent)
	{
		str << val;
		hasPercent = false;
	}
	return str.str();
}

String functionIterate0(String const & templ, int base, int count)
{
	std::stringstream str;
	if(count < 0)
	{
		for(int i=base; count < 0; --i, ++count )
		{
			str << functionSubst(templ, i);
		}
	}
	else
	{
		for(int i=base; count > 0; ++i, --count )
		{
			str << functionSubst(templ, i);
		}
	}
	return str.str();
}

String functionIterate1(String const & templ, int count)
{
	return functionIterate0(templ, 0, count);
}

String functionList0(String const & templ, int base, int count, String const & divisor)
{
	std::stringstream str;
	if(count < 0)
	{
		for(int i=base; count < 0; --i, ++count )
		{
			if(i) str << divisor;
			str << functionSubst(templ, i);
		}
	}
	else
	{
		for(int i=base; count > 0; ++i, --count )
		{
			if(i) str << divisor;
			str << functionSubst(templ, i);
		}
	}
	return str.str();
}

String functionList1(String const & templ, int count, String const & divisor)
{
	return functionList0(templ, 0, count, divisor);
}

String functionList2(String const & templ, int base, int count)
{
	return functionList0(templ, base, count, ", ");
}

String functionList3(String const & templ, int count)
{
	return functionList2(templ, 0, count);
}

String	functionListX0(String const & wrapping, String const & templ, int base, int count, String const & divisor)
{
	if(count <= 0) return String();
	return functionSubst(wrapping, functionList0(templ, base, count, divisor));
}

String	functionListX1(String const & wrapping, String const & templ, int count, String const & divisor)
{
	return functionListX0(wrapping, templ, 0, count, divisor);
}

String	functionListX2(String const & wrapping, String const & templ, int base, int count)
{
	return functionListX0(wrapping, templ, base, count, ", ");
}

String	functionListX3(String const & wrapping, String const & templ, int count)
{
	return functionListX2(wrapping, templ, 0, count);
}

String functionClassArgs0(String const & prefix, int n, String const & postfix)
{
	if(!n) return String();
	return prefix + functionList3("class Param%", n) + postfix;
}

String functionClassArgs1(int n, String const & postfix)
{
	return functionClassArgs0(String(), n, postfix);
}

String functionClassArgs2(String const & prefix, int n)
{
	return functionClassArgs0(prefix, n, String());
}

String functionClassArgs3(int n)
{
	return functionClassArgs0(String(), 0, String());
}

String functionSelArgs0(String const & prefix, int n, String const & postfix)
{
	if(!n) return String();
	return prefix + functionList3("Param%", n) + postfix;
}

String functionSelArgs1(int n, String const & postfix)
{
	return functionSelArgs0(String(), n, postfix);
}

String functionSelArgs2(String const & prefix, int n)
{
	return functionSelArgs0(prefix, n, String());
}

String functionSelArgs3(int n)
{
	return functionSelArgs0(String(), 0, String());
}

String functionFuncArgs0(String const & prefix, int n, String const & postfix)
{
	if(!n) return String();
	return prefix + functionList3("Param% p%", n) + postfix;
}

String functionFuncArgs1(int n, String const & postfix)
{
	return functionFuncArgs0(String(), n, postfix);
}

String functionFuncArgs2(String const & prefix, int n)
{
	return functionFuncArgs0(prefix, n, String());
}

String functionFuncArgs3(int n)
{
	return functionFuncArgs0(String(), 0, String());
}

String functionCallArgs0(String const & prefix, int n, String const & postfix)
{
	if(!n) return String();
	return prefix + functionList3("p%", n) + postfix;
}

String functionCallArgs1(int n, String const & postfix)
{
	return functionCallArgs0(String(), n, postfix);
}

String functionCallArgs2(String const & prefix, int n)
{
	return functionCallArgs0(prefix, n, String());
}

String functionCallArgs3(int n)
{
	return functionCallArgs0(String(), 0, String());
}



#endif //__MCS__FUNCTION_FACTORY_COLLECTION__HPP